package sintatico;

import java.util.Stack;

import semantico.SemanticAnalyzer;
import sintatico.submaquinas.SubMaquina;
import symbols.Tag;
import symbols.Token;

import lexico.*;

public class SyntaticAnalyzer {

	private SubMaquina maquinaAtual;

	private LexicalAnalyzer lexico;

	private Stack<SubMaquina> pilhaMaquinas;
	
	private SemanticAnalyzer semantico;

	private Token token;

	public SyntaticAnalyzer(String fileName) {
 		this.lexico = new LexicalAnalyzer(fileName);
		semantico = new SemanticAnalyzer(this, fileName);
		pilhaMaquinas = new Stack<SubMaquina>();
		maquinaAtual = SubMaquina.geraSubMaquina("programa", lexico);// maquina programa eh a principal
	}

	public boolean fazerAnalise() {
		ResultadoTransicao transicaoToken = null; // transicao com consumo de token
		ResultadoTransicao transicaoVazia = null; // transicao epsilon
		
		// Escreve o inicio do programa
		semantico.inicioPrograma();
		
		token = lexico.getNextToken();
		boolean read = true;

		/*
		 * Enquanto nao for o final do arquivo ou nao tiver transacao vazia com estado de aceitacao, 
		 * 	continuar lendo os tokens 
		 */
		while (token.tag != Tag.EOF || (transicaoVazia != null && !maquinaAtual.isEstadoAceitacao())) {
//			System.out.println("======================================================");
//			System.out.println("wordRead: " + lexico.wordRead);
//			System.out.println("line number: " + lexico.getLineNumber());
//			System.out.println("Tag: " + token.tag);
			read = true;
			
			// verifica transicao
			transicaoToken = maquinaAtual.fazTransicao(token);
			if (transicaoToken == null) { // nao ha transicao com consumo de token
				transicaoVazia = maquinaAtual.fazTransicao(null);
				if (transicaoVazia == null) { // faz transicao vazia
					if (maquinaAtual.isEstadoAceitacao() && pilhaMaquinas.size() > 0) {
						maquinaAtual = pilhaMaquinas.pop();
						read = false;
					} 
					else {
						return false;
					}
				} 
				else {
					// Faz a transicao sem consumo
					read = false;
					if (transicaoVazia.getSubmaquina() != null) {
						maquinaAtual.setEstadoAtual(transicaoVazia.getProximoEstado());
						pilhaMaquinas.push(maquinaAtual);
						maquinaAtual = SubMaquina.geraSubMaquina(transicaoVazia.getSubmaquina(), lexico);
					} 
					else {
						maquinaAtual.setEstadoAtual(transicaoVazia.getProximoEstado());
					}
					semantico.fazerAcaoSemantica(transicaoVazia.getAcaoSemantica());
				}
			} 
			else {
				// Faz a transicao com consumo
				maquinaAtual.setEstadoAtual(transicaoToken.getProximoEstado());
				semantico.fazerAcaoSemantica(transicaoToken.getAcaoSemantica());
			}

//			System.out.println("Maquina Atual ="+maquinaAtual.getClass());
//			System.out.println("Estado Atual = "+maquinaAtual.getEstadoAtual());
//			if (transicaoToken!=null) {
//				System.out.println("Transicao efetuada: " + transicaoToken);
//				System.out.println("Transicao efetuada: " + transicaoToken.getSubmaquina());
//				System.out.println("Acao semantica: "+transicaoToken.getAcaoSemantica());
//			}
//			else if(transicaoVazia!=null){
//				System.out.println("Transicao Vazia: " + transicaoVazia);
//				System.out.println("Transicao Vazia: " + transicaoVazia.getSubmaquina());
//				System.out.println("Acao semantica: "+transicaoVazia.getAcaoSemantica());
//			}
			
			if (read) {
				token = lexico.getNextToken();
			}
			transicaoVazia = maquinaAtual.fazTransicao(null);
		}
		if (maquinaAtual.isEstadoAceitacao()) {
			semantico.terminaPrograma();
			semantico.criaMaravilhosoArquivoASM();
			return true;
		} 
		else {
			return false;
		}
	}

	public Token getToken() {
		return token;
	}

	public void setToken(Token token) {
		this.token = token;
	}

	public LexicalAnalyzer getLexico() {
		return lexico;
	}

	public void setLexico(LexicalAnalyzer lexico) {
		this.lexico = lexico;
	}

	public SubMaquina getMaquinaAtual() {
		return maquinaAtual;
	}

	public void setMaquinaAtual(SubMaquina maquinaAtual) {
		this.maquinaAtual = maquinaAtual;
	}

}
